1 import sys, random
2
3 if sys.version_info.major > 2:
4 import tkinter as tk
5 else:
6 import Tkinter as tk
7
8 GREEN, BLACK, WHITE, DARK_GREEN, BLUE = "green", "black", "white", "dark green", "blue"
9 ZERO = 2
10 LOWER, UPPER = "lower", "upper"
11 HOME, AWAY = "Player 1", "Player 2"
12 START_SCORE = {HOME: 0, AWAY: 0}
13 MAX_SCORE = 7
14 SPEED = 20
15 FONT = "ms 50"
16 MAX_SPEED, PADDLE_SPEED = 15, 15
17
18
19 def str_dict(dic):
20 return "%s: %d, %s: %d" % (HOME, dic[HOME], AWAY, dic[AWAY])
21
22 def rand():
23 return random.choice(((1, 1), (1, -1), (-1, 1), (-1, -1)))
24
25
26
27 class Equitment(object):
28 def __init__(self, canvas, width, position, color):
29 self.can, self.w = canvas, width
30 self.x, self.y = position
31
32 self.Object = self.can.create_oval(self.x-self.w, self.y-self.w,
33 self.x+self.w, self.y+self.w, fill=color)
34 def update(self, position):
35 self.x, self.y = position
36 self.can.coords(self.Object, self.x-self.w, self.y-self.w,
37 self.x+self.w, self.y+self.w)
38 def __eq__(self, other):
39 overlapping = self.can.find_overlapping(self.x-self.w, self.y-self.w,
40 self.x+self.w, self.y+self.w)
41 return other.get_object() in overlapping
42
43 def get_width(self):
44 return self.w
45 def get_position(self):
46 return self.x, self.y
47 def get_object(self):
48 return self.Object
49
50 class PuckManager(Equitment):
51 def __init__(self, canvas, width, position):
52 Equitment.__init__(self, canvas, width, position, BLACK)
53
54 class Paddle(Equitment):
55 def __init__(self, canvas, width, position):
56 Equitment.__init__(self, canvas, width, position, GREEN)
57 self.handle = self.can.create_oval(self.x-self.w/2, self.y-self.w/2,
58 self.x+self.w/2, self.y+self.w/2, fill=DARK_GREEN)
59 def update(self, position):
60 Equitment.update(self, position)
61 self.can.coords(self.handle, self.x-self.w/2, self.y-self.w/2,
62 self.x+self.w/2, self.y+self.w/2)
63
64 class Background(object):
65 def __init__(self, canvas, screen, goal_w):
66 self.can, self.goal_w = canvas, goal_w
67 self.w, self.h = screen
68
69 self.draw_bg()
70
71 def draw_bg(self):
72 self.can.config(bg=WHITE, width=self.w, height=self.h)
73 d = self.goal_w/4
74 self.can.create_oval(self.w/2-d, self.h/2-d, self.w/2+d, self.h/2+d,
75 fill=WHITE, outline=BLUE)
76 self.can.create_line(ZERO, self.h/2, self.w, self.h/2, fill=BLUE)
77 self.can.create_line(ZERO, ZERO, ZERO, self.h, fill=BLUE)
78 self.can.create_line(self.w, ZERO, self.w, self.h, fill=BLUE)
79
80 self.can.create_line(ZERO, ZERO, self.w/2-self.goal_w/2, ZERO,
81 fill=BLUE)
82 self.can.create_line(self.w/2+self.goal_w/2, ZERO, self.w, ZERO,
83 fill=BLUE)
84
85 self.can.create_line(ZERO, self.h, self.w/2-self.goal_w/2, self.h,
86 fill=BLUE)
87 self.can.create_line(self.w/2+self.goal_w/2, self.h, self.w, self.h,
88 fill=BLUE)
89
90 def is_position_valid(self, position, width, constraint=None):
91 x, y = position
92
93 if constraint == None and self.is_in_goal(position, width):
94 return True
95 elif (x - width < ZERO or x + width > self.w or
96 y - width < ZERO or y + width > self.h):
97 return False
98 elif constraint == LOWER:
99 return y - width > self.h/2
100 elif constraint == UPPER:
101 return y + width < self.h/2
102 else:
103 return True
104
105 def is_in_goal(self, position, width):
106 x, y = position
107 if (y - width <= ZERO and x - width > self.w/2 - self.goal_w/2 and
108 x + width < self.w/2 + self.goal_w/2):
109 return HOME
110 elif (y + width >= self.h and x - width > self.w/2 - self.goal_w/2 and
111 x + width < self.w/2 + self.goal_w/2):
112 return AWAY
113 else:
114 return False
115
116 def get_screen(self):
117 return self.w, self.h
118 def get_goal_w(self):
119 return self.goal_w
120
121 class Puck(object):
122 def __init__(self, canvas, background):
123 self.background = background
124 self.screen = self.background.get_screen()
125 self.x, self.y = self.screen[0]/2, self.screen[1]/2
126 self.can, self.w = canvas, self.background.get_goal_w()/12
127 c, d = rand()
128 self.vx, self.vy = 4*c, 6*d
129 self.a = .99
130 self.cushion = self.w*0.25
131
132 self.puck = PuckManager(canvas, self.w, (self.y, self.x))
133
134 def update(self):
135
136 if self.vx > 0.25: self.vx *= self.a
137 if self.vy > 0.25: self.vy *= self.a
138
139 x, y = self.x + self.vx, self.y + self.vy
140 if not self.background.is_position_valid((x, y), self.w):
141 if x - self.w < ZERO or x + self.w > self.screen[0]:
142 self.vx *= -1
143 if y - self.w < ZERO or y + self.w > self.screen[1]:
144 self.vy *= -1
145 x, y = self.x+self.vx, self.y+self.vy
146
147 self.x, self.y = x, y
148 self.puck.update((self.x, self.y))
149
150 def hit(self, paddle, moving):
151 x, y = paddle.get_position()
152
153 if moving:
154 if (x > self.x - self.cushion and x < self.x + self.cushion or
155 abs(self.vx) > MAX_SPEED):
156 xpower = 1
157 else:
158 xpower = 5 if self.vx < 2 else 2
159 if (y > self.y - self.cushion and y < self.y + self.cushion or
160 abs(self.vy) > MAX_SPEED):
161 ypower = 1
162 else:
163 ypower = 5 if self.vy < 2 else 2
164 else:
165 xpower, ypower = 1, 1
166
167 if self.x + self.cushion < x:
168 xpower *= -1
169 if self.y + self.cushion < y:
170 ypower *= -1
171
172 self.vx = abs(self.vx)*xpower
173 self.vy = abs(self.vy)*ypower
174
175 def __eq__(self, other):
176 return other == self.puck
177 def in_goal(self):
178 return self.background.is_in_goal((self.x, self.y), self.w)
179
180 class Player(object):
181 def __init__(self, master, canvas, background, puck, constraint):
182 self.puck, self.background = puck, background
183 self.constraint, self.v = constraint, PADDLE_SPEED
184 screen = self.background.get_screen()
185 self.x = screen[0]/2
186 self.y = 60 if self.constraint == UPPER else screen[1] - 50
187
188 self.paddle = Paddle(canvas, self.background.get_goal_w()/7,
189 (self.x, self.y))
190 self.up, self.down, self.left, self.right = False, False, False, False
191
192 if self.constraint == LOWER:
193 master.bind('<Up>', self.MoveUp)
194 master.bind('<Down>', self.MoveDown)
195 master.bind('<KeyRelease-Up>', self.UpRelease)
196 master.bind('<KeyRelease-Down>', self.DownRelease)
197 master.bind('<Right>', self.MoveRight)
198 master.bind('<Left>', self.MoveLeft)
199 master.bind('<KeyRelease-Right>', self.RightRelease)
200 master.bind('<KeyRelease-Left>', self.LeftRelease)
201 else:
202 master.bind('<w>', self.MoveUp)
203 master.bind('<s>', self.MoveDown)
204 master.bind('<KeyRelease-w>', self.UpRelease)
205 master.bind('<KeyRelease-s>', self.DownRelease)
206 master.bind('<d>', self.MoveRight)
207 master.bind('<a>', self.MoveLeft)
208 master.bind('<KeyRelease-d>', self.RightRelease)
209 master.bind('<KeyRelease-a>', self.LeftRelease)
210
211 def update(self):
212 x, y = self.x, self.y
213
214 if self.up: y = self.y - self.v
215 if self.down: y = self.y + self.v
216 if self.left: x = self.x - self.v
217 if self.right: x = self.x + self.v
218
219 if self.background.is_position_valid((x, y),
220 self.paddle.get_width(), self.constraint):
221 self.x, self.y = x, y
222 self.paddle.update((self.x, self.y))
223 if self.puck == self.paddle:
224 moving = any((self.up, self.down, self.left, self.right))
225 self.puck.hit(self.paddle, moving)
226
227 def MoveUp(self, callback=False):
228 self.up = True
229 def MoveDown(self, callback=False):
230 self.down = True
231 def MoveLeft(self, callback=False):
232 self.left = True
233 def MoveRight(self, callback=False):
234 self.right = True
235 def UpRelease(self, callback=False):
236 self.up = False
237 def DownRelease(self, callback=False):
238 self.down = False
239 def LeftRelease(self, callback=False):
240 self.left = False
241 def RightRelease(self, callback=False):
242 self.right = False
243
244 class Home(object):
245 def __init__(self, master, screen, score=START_SCORE.copy()):
246 self.frame = tk.Frame(master)
247 self.frame.pack()
248 self.can = tk.Canvas(self.frame)
249 self.can.pack()
250 background = Background(self.can, screen, screen[0]*0.33)
251 self.puck = Puck(self.can, background)
252 self.p1 = Player(master, self.can, background, self.puck, UPPER)
253 self.p2 = Player(master, self.can, background, self.puck, LOWER)
254
255 master.bind("<Return>", self.reset)
256 master.bind("<r>", self.reset)
257
258 master.title(str_dict(score))
259
260 self.master, self.screen, self.score = master, screen, score
261
262 self.update()
263
264 def reset(self, callback=False):
265 """ <Return> or <r> key. """
266 if callback.keycode == 82:
267 self.score = START_SCORE.copy()
268 self.frame.destroy()
269 self.__init__(self.master, self.screen, self.score)
270
271 def update(self):
272 self.puck.update()
273 self.p1.update()
274 self.p2.update()
275 if not self.puck.in_goal():
276 self.frame.after(SPEED, self.update)
277 else:
278 winner = HOME if self.puck.in_goal() == AWAY else AWAY
279 self.update_score(winner)
280
281 def update_score(self, winner):
282 self.score[winner] += 1
283 self.master.title(str_dict(self.score))
284 if self.score[winner] == MAX_SCORE:
285 self.frame.bell()
286 self.can.create_text(self.screen[0]/2, self.screen[1]/2, font=FONT,
287 text="%s wins!" % winner)
288 self.score = START_SCORE.copy()
289 else:
290 self.can.create_text(self.screen[0]/2, self.screen[1]/2, font=FONT,
291 text="Point for %s" % winner)
292
293 def play(screen):
294 root = tk.Tk()
295 Home(root, screen)
296 root.mainloop()
297
298 if __name__ == "__main__":
299
300 screen = 700, 760
301
302 play(screen)